#include "wx/wxprec.h"

#if wxUSE_PROTOCOL

#include "wx/protocol/protocol.h"

#include "wx/url.h"

#include <stdlib.h>

/////////////////////////////////////////////////////////////////
// wxProtoInfo
/////////////////////////////////////////////////////////////////

/*
   --------------------------------------------------------------
   --------- wxProtoInfo CONSTRUCTOR ----------------------------
   --------------------------------------------------------------
*/

wxProtoInfo::wxProtoInfo( const wxChar *name, const wxChar *serv,
                          const bool need_host1, wxClassInfo *info )
  : m_protoname( name ),
    m_servname( serv ) {
  m_cinfo = info;
  m_needhost = need_host1;
  #if wxUSE_URL
  next = wxURL::ms_protocols;
  wxURL::ms_protocols = this;
  #else
  next = NULL;
  #endif
}

/////////////////////////////////////////////////////////////////
// wxProtocol ///////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////

#if wxUSE_SOCKETS
IMPLEMENT_ABSTRACT_CLASS( wxProtocol, wxSocketClient )
#else
IMPLEMENT_ABSTRACT_CLASS( wxProtocol, wxObject )
#endif

wxProtocol::wxProtocol()
#if wxUSE_SOCKETS
  : wxSocketClient()
#endif
{
}

#if wxUSE_SOCKETS
bool wxProtocol::Reconnect() {
  wxIPV4address addr;
  if( !GetPeer( addr ) ) {
    Close();
    return false;
  }
  if( !Close() ) {
    return false;
  }
  if( !Connect( addr ) ) {
    return false;
  }
  return true;
}

// ----------------------------------------------------------------------------
// Read a line from socket
// ----------------------------------------------------------------------------

/* static */
wxProtocolError wxProtocol::ReadLine( wxSocketBase *sock, wxString& result ) {
  static const int LINE_BUF = 4095;
  result.clear();
  wxCharBuffer buf( LINE_BUF );
  char *pBuf = buf.data();
  while( sock->WaitForRead() ) {
    // peek at the socket to see if there is a CRLF
    sock->Peek( pBuf, LINE_BUF );
    size_t nRead = sock->LastCount();
    if( !nRead && sock->Error() ) {
      return wxPROTO_NETERR;
    }
    // look for "\r\n" paying attention to a special case: "\r\n" could
    // have been split by buffer boundary, so check also for \r at the end
    // of the last chunk and \n at the beginning of this one
    pBuf[nRead] = '\0';
    const char *eol = strchr( pBuf, '\n' );
    // if we found '\n', is there a '\r' as well?
    if( eol ) {
      if( eol == pBuf ) {
        // check for case of "\r\n" being split
        if( result.empty() || result.Last() != _T( '\r' ) ) {
          // ignore the stray '\n'
          eol = NULL;
        }
        //else: ok, got real EOL
        // read just this '\n' and restart
        nRead = 1;
      } else { // '\n' in the middle of the buffer
        // in any case, read everything up to and including '\n'
        nRead = eol - pBuf + 1;
        if( eol[-1] != '\r' ) {
          // as above, simply ignore stray '\n'
          eol = NULL;
        }
      }
    }
    sock->Read( pBuf, nRead );
    if( sock->LastCount() != nRead ) {
      return wxPROTO_NETERR;
    }
    pBuf[nRead] = '\0';
    result += wxString::FromAscii( pBuf );
    if( eol ) {
      // remove trailing "\r\n"
      result.RemoveLast( 2 );
      return wxPROTO_NOERR;
    }
  }
  return wxPROTO_NETERR;
}

wxProtocolError wxProtocol::ReadLine( wxString& result ) {
  return ReadLine( this, result );
}

// old function which only chops '\n' and not '\r\n'
wxProtocolError GetLine( wxSocketBase *sock, wxString& result ) {
#define PROTO_BSIZE 2048
  size_t avail, size;
  char tmp_buf[PROTO_BSIZE], tmp_str[PROTO_BSIZE];
  char *ret;
  bool found;
  avail = sock->Read( tmp_buf, PROTO_BSIZE ).LastCount();
  if( sock->Error() || avail == 0 ) {
    return wxPROTO_NETERR;
  }
  memcpy( tmp_str, tmp_buf, avail );
  // Not implemented on all systems
  // ret = (char *)memccpy(tmp_str, tmp_buf, '\n', avail);
  found = false;
  for( ret = tmp_str; ret < ( tmp_str + avail ); ret++ )
    if( *ret == '\n' ) {
      found = true;
      break;
    }
  if( !found ) {
    return wxPROTO_PROTERR;
  }
  *ret = 0;
  result = wxString::FromAscii( tmp_str );
  result = result.Left( result.length() - 1 );
  size = ret - tmp_str + 1;
  sock->Unread( &tmp_buf[size], avail - size );
  return wxPROTO_NOERR;
#undef PROTO_BSIZE
}
#endif // wxUSE_SOCKETS

#endif // wxUSE_PROTOCOL
